<!DOCTYPE html><html><head><meta charset="utf-8"><title>植的博客</title><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="//cdn.bootcss.com/font-awesome/4.6.3/css/font-awesome.min.css">
<link rel="stylesheet" href="/css/base.css">
<script type="text/javascript">(function() {
  var hm = document.createElement("script");
  hm.src = "https://hm.baidu.com/hm.js?ad6863b0ceb3ebc04afed41dc020bd78";
  var s = document.getElementsByTagName("script")[0]; 
  s.parentNode.insertBefore(hm, s);
})();</script><script type="text/x-mathjax-config">MathJax.Hub.Config({
  tex2jax: {
    inlineMath: [['$','$'], ['\\(','\\)']],
    displayMath: [['$$', '$$'], ['\\[','\\]']],
    processEscapes: true,
    processEnvironments: true,
    skipTags: ['script', 'noscript', 'style', 'textarea', 'pre','code'],
    TeX: { equationNumbers: { autoNumber: "AMS" },
         extensions: ["AMSmath.js", "AMSsymbols.js"] }
  }
});</script><script src="//cdn.bootcss.com/mathjax/2.6.1/MathJax.js?config=default"></script><meta name="generator" content="Hexo 4.2.0"><link rel="alternate" href="/atom.xml" title="植的博客" type="application/atom+xml">
</head><body><header><div class="page"><nav><ul class="brand"><li><a href="/">植的博客</a></li></ul><ul class="blog"><!-- List other items on menu.--><li class="menu-item"><a class="on" href="/archives">存档</a></li><li class="menu-item"><a href="/tags">标签</a></li><li class="menu-item"><a href="https://sanduck.github.io/about/" target="_blank" rel="noopener">梦</a></li></ul><ul class="social"><li><a class="fa fa-github" href="https://github.com/yfwz100" target="_blank" rel="noopener"><i class="sr-only">github</i></a></li><li><a class="fa fa-git" href="http://git.oschina.net/zhi" target="_blank" rel="noopener"><i class="sr-only">git</i></a></li><li><a class="fa fa-weibo" href="http://weibo.com/yfwz100" target="_blank" rel="noopener"><i class="sr-only">weibo</i></a></li></ul></nav></div></header><div class="post wrap"><div class="page"><div class="post-heading"><h1>Go lang 使用感受</h1></div><div class="post-body"><p>使用 Go 语言进行开发已经两周了，学习过程中感觉 Go 语言借鉴了很多其他语言的概念，但是又推陈出新。Go 语言有几个语言特性我觉得还是很值得借鉴的~</p>
<a id="more"></a>
<h1>没有类，只有结构体</h1>
<p>Java 流行的原因是面向对象编程的概念普及。面向对象涉及到的三个概念确实使得编程更为方便，例如继承。但是继承也会引入很多问题，例如，继承的层级越多导致代码膨胀。而有时候你也不知道你应该有多少种抽象。而且，当一个类希望复用两个类的概念的时候，Java 并不能实现多重继承，只能通过组合模式以及代理模式实现多重继承。并且，多重继承用于代码复用被认为是一个反模式而被弃用。新的代码鼓励使用所谓 POJO （plain old java object）实现，并多用组合实现代码复用，使得代码复用较复杂。</p>
<p>Go 语言里没有类的概念，也就无所谓的继承。但是 Go 的结构体实现了类似继承的代码复用的特性。在结构体内声明匿名结构体，则视为该结构体“继承”了该结构体的内容，而且该结构体的所有属性、方法被默认地“继承”到新的结构体上，可以直接调用。正确地讲，这并不是真正的继承，而是一种概念复用。我们实现一个概念，实际上并不是代表我对这个概念名称有多执着，而是对其实现的认可。因此，Go 并没有实现概念的层级关系，也就没有所谓的“继承”关系了，而只是一种复用。并且，可以复用多个结构体。</p>
<p>如果了解了 Go 语言的接口以后，我觉得会更清晰。</p>
<h1>接口与实现</h1>
<p>Go 语言的接口和 C++、Java 等语言不同，接口的实现与否并不是在结构体声明的时候决定的，而是该结构体实现了接口所需要的所有函数，即视为实现了该接口。由此，我们可以做出一些很新颖的做法。例如，我们声明一个表达式类：</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> Expr <span class="keyword">interface</span> &#123;</span><br><span class="line">    String()  <span class="keyword">string</span></span><br><span class="line">    Compute() <span class="keyword">float64</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>上述代码如果用 C 语言来写，估计要声明一个联合体或者一个结构体，来描述常量的定义。而在 Go 语言中，则没有引入任何结构体定义，只是新增了一个概念，用 float64 来表达的常量表达式的概念：</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> ValExpr <span class="keyword">float64</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(v ValExpr)</span> <span class="title">String</span><span class="params">()</span></span> &#123;</span><br><span class="line">    <span class="keyword">return</span> fmt.Sprintf(<span class="string">"%.6f"</span>, <span class="keyword">float64</span>(v))</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(v ValExpr)</span> <span class="title">Compute</span><span class="params">()</span></span> &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">float64</span>(v)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>而上述代码清晰地表达了常量表达式本身就是一个常数，但在表达式的场合下，也是一个运算表达式。 Go 语言在概念的表达能力上要优于很多其他语言，十分值得其他语言借鉴。回到结构体实现的部分， Go 语言应该是面向概念和实现的语言，只要符合条件，都可以被认为是实现，从而简化了复用。</p>
<h1>并发控制</h1>
<p>Go 有着良好的并发控制，或者说作者一开始就是为多核时代的并发编程而设计的。 Go 从语言层面上支持多线程，关键字 <code>go</code> 可以为任意一个函数新建一个“线程”来执行。这里的“线程”并不是真的线程，而是一种轻量级并发单元。我们知道线程是进程的轻量级实现，但是也是由操作系统调度的。因此，线程的并行涉及到 CPU 的任务调度，这种调度是十分消耗资源的，涉及到上下文的切换等，尤其是 CPU 的多核环境。Go 使用了一种比线程更轻量级的实现，go routine ，即上述所谓的“线程”。Go routine 和真实的线程是多对多关系，并且 go routine 的数量远大于线程数量，Go routine 由内部调度器来执行，减少实际线程的切换。</p>
<p>这部分有很多良好的阅读材料就不再叙述：</p>
<ul>
<li><a href="http://shinley.com/" target="_blank" rel="noopener">《Go 语言圣经》</a></li>
<li><a href="http://www.kancloud.cn/kancloud/effective/72199" target="_blank" rel="noopener">《Effective Go》</a></li>
</ul>
<h1>吐槽：泛型</h1>
<p>Go 语言值得吐槽的地方恐怕在于泛型了。虽然 Go 灵活的接口机制使得很多泛型限制可以很好地实现，但这种机制只是单向的，并不能作为返回类型限制。例如，要实现一个简单的 HashMap，于是，我们定义方法：</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(s *HashSet)</span> <span class="title">Put</span><span class="params">(item HashItem, value <span class="keyword">interface</span>&#123;&#125;)</span></span> &#123;</span><br><span class="line">    <span class="comment">// ignore...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>其中 item 是一个带有 <code>hash()</code> 方法的接口：</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> HashItem <span class="keyword">interface</span> &#123;</span><br><span class="line">    Hash() <span class="keyword">int</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>这个接口定义很完美，如果是 C++，要做 hash key 还要实现一个接口体之类的，而 Go 直接通过接口定义概念了。然而，作为 value 应该是没有什么输入限制的，所以，value 被定义为万能类型 <code>interface{}</code> 。然而，当我们定义取出函数的时候：</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(s *HashSet)</span> <span class="title">Get</span><span class="params">(item HashItem)</span> <span class="title">interface</span></span>&#123;&#125; &#123;</span><br><span class="line">    <span class="comment">// ignore...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>存进去的 value 的类型丢失了，我们只能得到万能类型 <code>interface{}</code>，这没什么用。因此， Go 语言的接口可以在一定程度上替代泛型的作用，但是这种泛型是单向的，是限制输入的，对于输出毫无办法。类型推断到这里就很糟糕了。于是，在基础的 Go 集合类里，大多数都是使用万能类型作为输出操作的类型，使得每次调用还要进行强制类型转换。这对于以集合作为输出的函数就是一个灾难，类型抹除使得调用者根本不知道返回的是什么，除非注释。为了让我的代码看起来容易理解，我还特意<a href="http://git.oschina.net/zhi/expr.go" target="_blank" rel="noopener">新建了两个类来封装用到的集合</a>……</p>
</div><div class="post-meta">由 <span class="author"><a href="mailto: yfwz100@yeah.net">Zhi</a></span> 写于 <span class="date">2017年4月16日</span> ·<span class="tags"><a class="tag" href="/tags/编程语言/">编程语言</a></span></div><div class="cloud-tie-wrapper" id="cloud-tie-wrapper"></div><script type="text/javascript" src="https://img1.cache.netease.com/f2e/tie/yun/sdk/loader.js"></script><script type="text/javascript">var cloudTieConfig = {
  url: document.location.href, 
  sourceId: "",
  productKey: "2311812cfc39469b8c071ba8f99d562e",
  target: "cloud-tie-wrapper"
};
var yunManualLoad = true;
Tie.loader("aHR0cHM6Ly9hcGkuZ2VudGllLjE2My5jb20vcGMvbGl2ZXNjcmlwdC5odG1s", true);</script><script type="text/javascript" src="https://img1.cache.netease.com/f2e/tie/yun/sdk/loader.js"></script></div></div><footer><div class="page"><address class="author">&copy; Zhi</address><div class="hexo-powered">博客由 <a href="http://www.hexo.io" target="_blank" rel="noopener">Hexo</a> 驱动</div><div class="rss"><a class="fa fa-rss-square" href="/atom.xml"></a></div></div></footer></body></html>